//
//  KLineData.cpp
//  Chart
//
//  Created by CaoJun on 13-6-17.
//
//

#include "KLineData.h"
#include "json.h"
#include "KLineSQLite.h"

#include <vector>
#include <string>
#include <cmath>

using namespace ytx;
using namespace std;


string LT = "lt";
string GT = "gt";


KLineOption :: KLineOption(string tradedate, string tradeEndDate, string limit, string sort, bool isCalculate) : tradedate(tradedate), tradeEndDate(tradeEndDate), limit(limit), sort(sort), isCalculate(isCalculate)
{

}

KLineOption :: KLineOption(bool isCalculate) : isCalculate(isCalculate), limit(ConvertToString(Config::getInstance()->kLine.landscapBaseDrawCount)),
sort("lt"), tradedate(""), tradeEndDate("0")
{

}

KLineData * KLineData::getObservable()
{
    return this;
}

KLineData :: KLineData(string id, string currentDate, LineType::LineType quotationType, string marketType, int toFix, string name, string nickName, bool hasVolume, int limit, int threshold, int buffer, YTXTime tradedate) :
Quotation(id, currentDate, quotationType, marketType, hasVolume, toFix)
, UserGraphInterface(id, quotationType)
, IndexConfigInterface(id, quotationType)
, dataEmptyFlagTo(false), dataEmptyFlagFrom(false), tradedateTo("")
, tradedateFrom(""), buffer(1), closeDataFlag(false), indexType(IndexType::EMPTY)
{
    setDrawingCount(Config::getInstance()->kLine.portraitBaseDrawCount);
    setRow(Rect(4,4,2));
    setColumn(Rect(4,4,2));
    setQuotationName(name);
    setNickName(nickName);
    setDateTo(tradedate);
    setLimit(limit);
//    setOriginShowCount(showCount);

    setThreshold(threshold);
    setBuffer(buffer);
    setType(QuotationType::KLine);

}

KLineData :: ~KLineData()
{
    listenOff();
}

void KLineData :: requestData(const string tag, const DataOption & dataOption, const string acceptableContent)
{
    fire("request.start", NULL);
    Quotation::requestData(tag, dataOption);
}

void KLineData :: callback(string tag, string data)
{
    YTXJson::Reader reader;
    YTXJson::Value value;
    
    try {
        if(reader.parse(data, value))
        {
            fire("request.stop", NULL);
            if(tag == "getKFromTradeDateFrom"){// 历史
                responseKLine(value["data"], value["errorCode"], tag);
            }
            else if(tag == "getKFromTradeDateTo"){// 当日
                responseKLine(value["data"], value["errorCode"], tag);
            }
        }
    } catch (void * __attribute__((annotate("oclint:suppress[unused local variable]"))) ex) {
        fire("request.stop", NULL);
    }
}

void KLineData::firstFailRequestHandler(string tag)
{
    if (tag == "requestKLine" && !hasData())
    {
        fire("firstFail", NULL);
    }
}

void KLineData::onFail(string tag)
{
    fire("request.stop", NULL);
    if(tag == "getKFromTradeDateFrom"){
        bufferFlag = true;
        dataEmptyFlagFrom = false;
    }
    if(tag == "getKFromTradeDateTo"){
        dataEmptyFlagTo = false;
    }
    fire(tag+".stop", NULL);
}

bool LessSort (KLineItem a,KLineItem b) { return (a.dateTime<b.dateTime); }
void KLineData::responseKLine(YTXJson::Value data, YTXJson::Value info, string tag){
    int len = data.size();
    if (len == 0) {
        if (tag == "getKFromTradeDateFrom") {
            dataEmptyFlagFrom = true;
        }
        if ( !LineType::isMinuteLine(quotationType) ) {
            getCloseNewK();
        }
        return;
    }

    YTXJson::Value item;
    double o,h,l,c,va,vo,hold,upDrop, percent,sTradedate, isCurrent;
    string sPercent, itemTradeDate;
    YTXTime tradedate;
    vector<KLineItem> result;
    vector<string> dates;

    for (YTXJson::Value::iterator it = data.begin(); it != data.end(); ++it) {
        item = *it;
        itemTradeDate = item["TrdDy"].asString();
        o = item["OpPri"].asDouble();
        h = item["HiPri"].asDouble();
        l = item["LoPri"].asDouble();
        c = item["ClPri"].asDouble();
        vo = item.isMember("Vol") ? item["Vol"].asDouble() : 0;
        va = item.isMember("Tuov") ? item["Tuov"].asDouble() : 0;
        hold = item.isMember("OpInt") ? item["OpInt"].asDouble() : 0;
        sTradedate = item["KlTm"].asDouble();
        tradedate = YTXTime(YTXTime(sTradedate).toStringGMT8YYYYMMddHHmmss());
        upDrop = item.isMember("updrop") ? item["updrop"].asDouble() : 0;
        sPercent = item.isMember("percent") ? item["percent"].asDouble() : 0;
        isCurrent = item["IsCurrent"].asDouble();
        std::string::size_type pos = sPercent.find('%');
        if (pos == std::string::npos) {
            percent = Tools::parseDouble(sPercent);
        } else {
            percent = Tools::parseDouble(sPercent.replace(pos, 1, ""));
        }

        KLineItem ochl = {
            itemTradeDate,
            o,
            h,
            l,
            c,
            vo,
            va,
            hold,
            tradedate,
            upDrop,
            percent,
            0,
            0,
            isCurrent
            //index : it - databegin(),
        };

        dates.push_back(tradedate.toStringYYYYMMddHHmmss());
        result.push_back(ochl);
    }
    
    sort(dates.begin(), dates.end());//从小到大排序
    sort(result.begin(), result.end(), LessSort);//

    if (tag == "getKFromTradeDateFrom") {
//        if (dateData.size() && dates.front() == dateData.front()) {// 返回重复数据，没有更新  dates.back() == dateData.back()
//            dataEmptyFlagFrom = true;
//            calculateK();
//            return;
//        }
        KLineSQLite sql;
        if (len == limit && dateFrom.toSeconds() != 0) {
            sql.clearTable(KLineSQLite::getTableName(this));
        }
        sql.createKLineDataTableIfNotExit(sql.getTableName(this));
        sql.insertData(KLineSQLite::getTableName(this) , result);
    }
    setK(result, dates);
}

void KLineData::setK(vector<KLineItem> & data, vector<string> & dateData)
{
    bool initNeedGetNewCloseK = !hasDataWithoutClose();
    bool isCalculate = concatData(data, dateData);//拼接 新返回的 和 本地的 数据

    if(isCalculate){//已拼接 则重绘
        calculateK();
    }

    if(initNeedGetNewCloseK){
        getCloseNewK();
    }
    bufferFlag = true;
    dataEmptyFlagFrom = false;
    dataEmptyFlagTo = false;
}

void KLineData::listenOn()
{
    //listenOff();
}

void KLineData::listenOff()
{

}

bool KLineData::checkNeedGetNewK(){
    return false;
}

void KLineData::getCloseNewK(){

}

unsigned int KLineData::filterDataFrom(vector<KLineItem> & data, vector<string> & dates)
{
//    if (dateData.size() > 0 && data.back().dateTime == YTXTime(this->dateData[0])) {
//        data.pop_back();
//        dates.pop_back();
//    }
    
    return (unsigned int)data.size();
}

unsigned int KLineData::filterDataTo(vector<KLineItem> & data, vector<string> & dates)
{
    
    
    return (unsigned int)data.size();
}

bool KLineData::concatCloseNewData(vector<KLineItem> & data, vector<string> & dates)
{
    if(data.size() == 0){
        return false;
    }
    
    if (data.size() == 1
        && hasData()
        && data.back().dateTime == kLineData.back().dateTime
        && data.back().upDrop == kLineData.back().upDrop
        && data.back().close == kLineData.back().close
        && data.back().totalVolume == kLineData.back().totalVolume) {
        return false;
    }
    
    if(closeDataFlag){
        kLineData.pop_back();
        dateData.pop_back();
    }
    closeDataFlag = true;
    kLineData.insert(kLineData.end(), data.begin(), data.end());
    dateData.insert(dateData.end(), dates.begin(), dates.end());
    filterLastSameData();
    fire("kLineData.update", NULL);
    return true;
}


void KLineData::filterLastSameData() {//若倒数第1个和前面的数据相同则只保留一个
    
    if (!hasData()) {
        return;
    }
    
    KLineItem last = kLineData.back();
    
    for (int i = getDataLength() - 2; i > 0 && i > getDataLength() - 4  ; i--) {
        KLineItem current = kLineData[i];
        if (current.isEqual(last)) {
            kLineData.erase(kLineData.begin()+i);
            dateData.erase(dateData.begin()+i);
            return;
        }
        last = current;
    }
}

bool KLineData::concatData(vector<KLineItem> & data, vector<string> & dates)
{
    vector<KLineItem>::size_type dataLen = data.size();
    if (!dataLen) {
        return false;
    }
    
    if (!hasData() || data.back().dateTime <= YTXTime(dateData[0]) ) {
        //向前插入
        dataLen = filterDataFrom(data, dates);
        if (!dataLen) {
            return false;
        }
        
        if (quotationType == LineType::L1w) {
            YTXTime currentDate = kLineData.front().dateTime;
            YTXTime firstDate = data.back().dateTime;
            
            if (currentDate.start_of_week_since_monday().date() == firstDate.start_of_week_since_monday().date()) {
                kLineData.erase (kLineData.begin());
                dateData.erase (dateData.begin());
            }
        }
        
        kLineData.insert(kLineData.begin(), data.begin(), data.end());
        dateData.insert(dateData.begin(), dates.begin(), dates.end());
        
    } else if (data[0].dateTime - hours(8) > getTradedate()) {
        KLineItem backItem;
        string backDate = "";
        
        dataLen = filterDataTo(data, dates);
        if (!dataLen) {
            return false;
        }
        
        if (closeDataFlag) {
            backItem = kLineData.back();
            kLineData.pop_back();
            backDate = dateData.back();
            dateData.pop_back();
            closeDataFlag = false;
        }
        
        kLineData.insert(kLineData.end(), data.begin(), data.end());
        dateData.insert(dateData.end(), dates.begin(), dates.end());
        
        if (backDate != "" && YTXTime( backItem.dateTime ) > YTXTime( kLineData.back().dateTime ) ) {
            closeDataFlag = true;
            kLineData.push_back(backItem);
            dateData.push_back(backDate);
        }
    } else {
        if (LineType::isMinutekLine(quotationType)) {
            vector<std::string>::iterator dataIt = dateData.begin();
            for (vector<KLineItem>::iterator it = kLineData.begin(); it != kLineData.end(); ++it, ++dataIt) {
                KLineItem item = *it;
                if(item.dateTime > data.back().dateTime ){
                    kLineData.insert(it, data.begin(), data.end());
                    dateData.insert(dataIt, dates.begin(), dates.end());
                    break;
                }
            }
        }
    }
    
    filterLastSameData();
    fire("kLineData.update", NULL);
    return true;
    
}

void KLineData::update(){
    calculateK();
}

void KLineData::controlBuffer(int startIndex)
{
    //int len = getDataLength();
    //由contrller控制
    if(bufferIsEnough(startIndex)){
        bufferFlag = true;
    }
    else{
        if(bufferFlag){
            bufferFlag = false;
            getKFromTradeDateFrom();
        }
    }

    // if(len >= threshold && startIndex == 0)
    // {
    //     //数据已满
    // }
}

void KLineData::calculateK(){


    if(hasData()){
//        string time = dateData.back();

        tradedateFrom = ConvertToString((YTXTime(dateData.front())-hours(8)).toSeconds());
        tradedateTo = ConvertToString((YTXTime(dateData.back())-hours(8)).toSeconds());

//        drawingCount = showCount;
        fire("load", NULL);
    }

}

bool KLineData::bufferIsEnough(int startIndex)
{
//    NSLog(@"--%d", getDataLength());
    return startIndex > buffer;// || getDataLength() >= threshold;
}

bool KLineData::hasData() const
{
    return kLineData.size() > 0;
}

bool KLineData::hasData(int index) const
{
    if(hasData()){
        int count = (int)kLineData.size();
        return 0 <= index && index < count;
    }
    return  false;
}


KLineOption KLineData::checkDataByTradedate(YTXTime tradedate) const
{
    //    date tDate = requestDate.date();
    //
    //    if(tDate.year() == 1970 && tDate.month() == 1 && tDate.day() == 1){
    //
    //    }
    //    else{
    //        requestDate = tradedate - minutes(0);
    //        //赋值
    //    }


    int i = getDataLength() - 1 ;
    bool isCalculate = true;
    if(closeDataFlag){
        i -= 1;
    }

//    for (;i > -1; i--)
//    {
//        YTXTime pDate = getTradedate(i);
//        if(!pDate.is_not_a_date_time() && tradedate >= pDate)
//        {
//            break;
//        }
//    }

    if(i == -1){
        isCalculate = false;
    }
    // else if ( i >= buffer){}

    long resultTradedate = LineType::isMinuteLine(quotationType) ? YTXTime(tradedate.toStringYYYYMMddHHmmss()).toSeconds() : YTXTime(tradedate.toStringYYYYMMdd()).toSeconds();

//    string pp = tradedate.toStringYYYYMMddHHmmss();

    return KLineOption(ConvertToString(resultTradedate),"0", ConvertToString(limit), GT, isCalculate);
}

YTXTime KLineData :: getTradedate(int index) const
{
    if (hasData(index)) {
        if (kLineData.size()) {//bool MACD::hasData() const
            return kLineData[index].dateTime;
        } else {
            return YTXTime::not_a_date_time();
        }
        
    }
    return YTXTime::not_a_date_time();
}

YTXTime KLineData :: getTradedate() const
{
    if (hasDataWithoutClose()) {
        return ((YTXTime)kLineData[dateData.size() - 2].dateTime) - hours(8);
        //Tools::String2PTime(dateData[dateData.size() - (closeDataFlag && dateData.size() > 1 ? 2 : 1)]);
    }
    return YTXTime::not_a_date_time();
}


void KLineData::getKFromTradeDateFrom()
{
    getKFromTradeDateFrom(limit);
}

// 历史K线
void KLineData::getKFromTradeDateFrom(int limit)
{
    if (dataEmptyFlagFrom) {
        return;
    }
    
    limit = (limit <= 0 ? this->limit : limit);
    long tempTradedate = LineType::isMinuteLine(quotationType) ? YTXTime(YTXTime(currentDate).toStringYYYYMMddHHmmss()).toSeconds() : YTXTime(YTXTime(currentDate).toStringYYYYMMdd()).toSeconds();

    string tradedate = tradedateFrom != "" ? tradedateFrom :ConvertToString(tempTradedate);

    vector<KLineItem> kLineItems;

    KLineSQLite kLineSQLite;

    YTXTime lastestTime = YTXTime::not_a_date_time();
    
    bool result = kLineSQLite.getDataFromDB(KLineSQLite::getTableName(this), YTXTime(Tools::parseLong(tradedate)+hours(8)), &kLineItems, limit);
    
    if (result && kLineItems.size()) {
        requestKLine(KLineOption(tradedate, ConvertToString(kLineItems.back().dateTime.toSeconds()-hours(8)), ConvertToString(limit), LT), "getKFromTradeDateFrom");
    } else{
        requestKLine(KLineOption(tradedate, "0", ConvertToString(limit), LT), "getKFromTradeDateFrom");
    }
}

// 当日K线
void KLineData::getKFromTradeDateTo()
{
    if (dataEmptyFlagTo) {
        return;
    }
    YTXTime latestTradedate = getTradedate();
    if (!latestTradedate.is_not_a_date_time()) {
        string tempTradedate = ConvertToString(LineType::isMinuteLine(quotationType) ? YTXTime(latestTradedate.toStringYYYYMMddHHmmss()).toSeconds() : YTXTime(latestTradedate.toStringYYYYMMdd()).toSeconds());
        requestKLine(KLineOption(tempTradedate, "-1", "0", GT), "getKFromTradeDateTo");
    } else {
        requestKLine(KLineOption("0", "-1", "0", GT), "getKFromTradeDateTo");
    }
}

void KLineData::getK()
{
    if(!hasDataWithoutClose()){
        //Attention: 要+8个小时来计算
        dateTo = YTXTime().GMT8();// 初始为当前时间
    }
    KLineOption option = checkDataByTradedate(dateTo);

    if(option.isCalculate){
        calculateK();
        checkNeedGetNewK();
    }
    else{
        KLineSQLite kLineSQLite;

        kLineSQLite.createKLineDataTableIfNotExit(kLineSQLite.getTableName(this));

        YTXTime lastestTime = YTXTime::not_a_date_time();

        YTXTime tradedate = option.tradedate == "" ? dateTo : YTXTime(Tools::parseLong(option.tradedate));

        bool isInRange = kLineSQLite.checkInRange(KLineSQLite::getTableName(this), &lastestTime, this, tradedate);

        // 本地有无数据
        if (lastestTime.is_not_a_date_time()) {
            // 直接去组装分时数据。换组装方法
            if (quotationType != LineType::L1w && quotationType != LineType::L1d) {
                getKFromTradeDateTo();// 当日K线
            }
            getKFromTradeDateFrom();// 历史K线
        }
        else{
            if (!isInRange) {////只有历史数据，没有需要的新数据
                getKFromTradeDateFrom();// 历史K线
            }
            else {//有数据且为最新数据
                //Attention: 要减去8个小时来计算
                bool cacheInvalidation = isCacheInvalidation(lastestTime, YTXTime(currentDate));

                if (!cacheInvalidation) {
                    kLineSQLite.clearTable(KLineSQLite::getTableName(this));
                    getKFromTradeDateFrom();// 历史K线
                    return;
                }
            }
            
            if (quotationType != LineType::L1w && quotationType != LineType::L1d) {
                getKFromTradeDateTo();// 当日K线
            }
            vector<KLineItem> kLineItems;

            kLineSQLite.getDataFromDB(KLineSQLite::getTableName(this), tradedate, &kLineItems, option.limit != "" ? Tools::parseInt(option.limit) : 300);

            vector<string> dates = transDatesFromKLineItems(kLineItems);
            setK(kLineItems, dates);
            checkNeedGetNewK();
        }
    }
}

void KLineData::calculateTodayKLine()
{
    
}

bool KLineData::isCacheInvalidation(YTXTime dataTime, YTXTime now)
{
    if (LineType::isMinuteLine(quotationType)) {
        return dataTime + minutes(6000 * LineType::getRequestParamOfQuotationType(quotationType) ) > now;
    }
    else {
        return dataTime + days(6000 * LineType::getRequestParamOfQuotationType(quotationType) ) > now;
    }

    return false;
}

void KLineData::requestKLine(const KLineOption option, string tag)//option(tradedate, tradeEndDate == "-1" ? "0": tradeEndDate, ConvertToString(limit), LT)
{
    int type = LineType::getRequestParamOfQuotationType(quotationType);

    map<string, string> data;

    data.insert(make_pair("Ei", ConvertToString(id)));

    if (option.limit != "") {
        data.insert(make_pair("Lmt", option.limit));
    }
    data.insert(make_pair("Period", ConvertToString(type)));

    data.insert(make_pair("QryTm", option.tradedate));
    
    if (quotationType != LineType::L1w && quotationType != LineType::L1d && option.tradeEndDate != "-1") {
        data.insert(make_pair("QryEndTm", option.tradeEndDate));
    }

    data.insert(make_pair("Sort", option.sort));

    Config * config = Config::getInstance();

    string routing = tag == "getKFromTradeDateFrom"?config->communicate.hisKdataRouting : config->communicate.mtdataRouting;

    DataOption dataOption = createOption(data, routing);

    NSLog(@"--requestKLine");
    requestData(tag, dataOption, "text/plain");
}



vector<string> KLineData::transDatesFromKLineItems(vector<KLineItem> & kLineItems){
    vector<string> result;

    for (vector<KLineItem>::iterator it = kLineItems.begin(); it!= kLineItems.end(); ++it) {
        result.push_back( LineType::isMinuteLine(quotationType) ? (*it).dateTime.toStringYYYYMMddHHmmss() : (*it).dateTime.toStringYYYYMMdd() );
    }

    return result;
}


string KLineData::getCrosshairYData(int index)
{
    if(hasData(index)){
        return Tools::Convert2FixString(kLineData[index].close, toFix);
    }
    return "";
}

UpDropStatus::UpDropStatus KLineData::getUpDropStatus(int index)
{
    if(kLineData[index].close >= kLineData[index].open){
        return UpDropStatus::Up;
    }
    return UpDropStatus::Drop;
}

std::pair<double, double> KLineData::getAdjustedDataMinMax(int startIndex, int endIndex, const Rectangle & rect) const
{
    pair<double, double> minMax = getDataMinMax(startIndex, endIndex);

    double min = minMax.first;
    double max = minMax.second;

    double p = abs(max - min) / rect.getRectHeight() *  Config::getInstance()->style.size * 2;
    max += p;
    min -= p;

    return make_pair(min, max);
}

pair<double, double> KLineData::getDataMinMax(int startIndex, int endIndex) const
{
    auto minMax = Quotation::getDataMinMax(startIndex, endIndex);
    
    if (holdingPositionInfos.size()) {
        auto res = minmax_element(holdingPositionInfos.begin(), holdingPositionInfos.end(), [](HoldingPositionInfo a, HoldingPositionInfo b) {return get<0>(a) < get<0>(b);});
        minMax.first = min(minMax.first, get<0>(*(res.first)));
        minMax.second = max(minMax.second, get<0>(*(res.second)));
    }
    
    return minMax;
}

std::vector<std::pair<double, std::string> > KLineData::getLeftSideText(int startIndex, int endIndex, const Rectangle & rect, const Style & style) const
{
    vector<std::pair<double, std::string> > result;

    double min = rect.getMin();
    double max = rect.getMax();
    int i = 0, column = rect.getColumn();
    double para = (max - min) / column;

    for(;i <= column;i++){
        result.push_back(make_pair(min + i * para, style.sideTextColor));
    }

    return result;
}

const vector<string> & KLineData::getBottomSideText(SizeType::SizeType) const
{
    return this->dateData;
}

int KLineData :: getLimit()
{
    return limit;
}

void KLineData :: setLimit(int limit)
{
    this->limit = limit;
}

int KLineData :: getThreshold()
{
    return threshold;
}
void KLineData :: setThreshold(int threshold)
{
    this->threshold = threshold;
}

int KLineData :: getBuffer()
{
    return buffer;
}
void KLineData :: setBuffer(int buffer)
{
    this->buffer = buffer;
}

void KLineData::setLineInfo(IndexData * indexData)
{
    indexType = indexData->indexType;
    lineData = indexData->lineData;
    titleType = indexData->titleType;
    infoTitle = indexData->getTopSideInfoText();
    this->indexData = indexData->indexData;
//    redrawFlag = true;
}

//int KLineData::fixStartIndex(int index) const
//{
//    int showCountValue = fixShowCount(showCount);
//
//    return Tools::between(0, getDataLength() - fixShowCount(showCount), index);
//}
//
//void KLineData::turn(int step)
//{
//    if(!hasData()){
//        return;
//    }
//    int tempStartIndex = startIndex + step;
//    setStartIndex(fixStartIndex(tempStartIndex));
//    calculateK();
//}
//
//void KLineData::turnLeft(int step)
//{
//    turn(-abs(step));
//}
//
//
//void KLineData::turnRight(int step)
//{
//    turn(+abs(step));
//}
//
//void KLineData::zoom(int step)
//{
//    if(!hasData()){
//        return;
//    }
//    int max = Config::getInstance()->kLine.max;
//    int min  = Config::getInstance()->kLine.min;
//
//    int tempShowCount = showCount - step;
//    int tempStartIndex = startIndex + step;
//    if(tempShowCount < min || tempShowCount > max || tempStartIndex < 0 || tempStartIndex > getDataLength() - tempShowCount){
//        return;
//    }
//
//    setShowCount(tempShowCount);
//    setStartIndex(tempStartIndex);
//
//    calculateK();
//}
//
//
//void KLineData::zoomIn(int step)
//{
//    zoom(-abs(step));
//}
//
//void KLineData::zoomOut(int step)
//{
//    zoom(+abs(step));
//}
//
//void KLineData::changeShowCount(int showCount)
//{
//    zoom(this->showCount - showCount);
//}
//
//void KLineData::changeShowCount()
//{
//    //min(originShowCount, getDataLength())
//    changeShowCount(originShowCount);
//}
//
//void KLineData::moveToLast(){
//    if(getDataLength() - showCount < 0){
//        return;
//    }
//    turn(getDataLength() - showCount - this->startIndex);
//}

double KLineData::getCrosshairXValue(int index)
{
    if(hasData(index)){
        return kLineData[index].close;
    }
    return 0;
}

KLineItem KLineData::getOCHL(vector<KLineItem> data)
{
    if(data.size() == 0)
    {
        KLineItem ret = {currentDate, numeric_limits<double>::quiet_NaN() };
        return ret;
    }
    return getOCHL(data, data[0].open);
}

KLineItem KLineData::getOCHL(vector<KLineItem> data, double preClose)//合并最后时间段的开盘价最高价等
{
    if(data.size() == 0)
    {
        KLineItem ret = {currentDate, numeric_limits<double>::quiet_NaN() };
        return ret;
    }
    int len = (int)data.size();
    string itemTradeDate = data.back().tradeDate;//data[0].tradeDate;//
    double open = data[0].open;
    double close = data[len - 1].close;
    double high = (*std::max_element(data.begin(), data.end(), KLineData::compareHigh)).high;
    double low = (*std::min_element(data.begin(), data.end(), KLineData::compareLow)).low;
    
    double updrop = close - preClose;
    double percent = (close / preClose - 1) * 100;
    YTXTime dateTime = data[len -1].dateTime;
    
    double sumValue=0, sumVolume =0;
    vector<KLineItem>::iterator it = data.begin();
    for (; it != data.end(); ++it) {
        KLineItem item = *it;
        if (item.tradeDate == currentDate) {
            sumVolume += item.totalVolume;
            sumValue += item.totalValue;
        } else {
            sumVolume += item.volume;
            sumValue += item.value;
        }
    }

    KLineItem ochl = {
        itemTradeDate,
        open,
        high,
        low,
        close,
        data.back().volume,
        data.back().value,
        data.back().totalHold,
        dateTime,
        updrop,
        percent,
        sumVolume,
        sumValue,
    };

    return ochl;

}

KLineItem KLineData::getOCHL(YTXTime limit1, YTXTime limit2)// week;
{
    KLineItem nilItem = {currentDate, numeric_limits<double>::quiet_NaN() };
    if(!hasData()){
        return nilItem;
    }

    vector<KLineItem> data;
    KLineItem firstItem = nilItem;
    int firstIndex = -1;
    int len = getDataLength();

    for (vector<KLineItem>::reverse_iterator it = kLineData.rbegin(); it != kLineData.rend(); ++it) {
        KLineItem item = (*it);
        if(item.dateTime >= limit1 && item.dateTime <= limit2){
            data.insert(data.begin(), item);
            firstItem = item;
            firstIndex = len - (int)(it - kLineData.rbegin()) - 1;
        }
    }



    if(std::isnan(firstItem.open))
    {
        return firstItem;
    }

    double preClose = firstIndex == 0 ? firstItem.open : kLineData[firstIndex - 1].close;

    return getOCHL(data, preClose);
}

KLineItem KLineData::getOCHL(YTXTime limit)
{
    return getOCHL(limit, YTXTime::max_date_time());
}

KLineItem KLineData::getOCHLByWeek(YTXTime now, YTXTimeDuration openTime)
{
    //周日是开始
    YTXTime date_monday = now.start_of_week_since_monday();

    YTXTime openTime_byMonday = YTXTime::initByDateAndTimeOfDay(date_monday.date(), openTime);

    return getOCHL(openTime_byMonday, now);

}

KLineItem KLineData::getOCHLByMonth(YTXTime now, YTXTimeDuration openTime)
{
    YTXTime data_firstDayOfMonth = now.start_of_month();

    YTXTime openTime_firstDayOfMonth = YTXTime::initByDateAndTimeOfDay(data_firstDayOfMonth.date(), openTime);

    return getOCHL(openTime_firstDayOfMonth, now);

}

bool KLineData::compareHigh(KLineItem a, KLineItem b)
{
    return a.high < b.high;
}

bool KLineData::compareLow(KLineItem a, KLineItem b)
{
    return a.low < b.low;
}

void KLineData::cleanData(){
//    startIndex = -1;
    closeDataFlag = false;
    dataEmptyFlagTo = false;
    dataEmptyFlagFrom = false;
    Quotation::cleanData();
    KLineSQLite kLineSQLite;
    kLineSQLite.clearTable(kLineSQLite.getTableName(this));
}


bool KLineData::hasDataWithoutClose() const{
    return hasData() && (closeDataFlag ? getDataLength() > 1 : true );
}

KLineItem KLineData::getLastItemWithoutClose() const{
    KLineItem nilItem = {currentDate, numeric_limits<double>::quiet_NaN() };
    if (hasData()){
        return closeDataFlag && getDataLength() > 1 ? kLineData[getDataLength() - 2]: kLineData[getDataLength() - 1];
    }
    return nilItem;
}


void KLineData::clearAllData()
{
    closeDataFlag = false;
    lineData.clear();
    kLineData.clear();
    dateData.clear();
//    QKData.clear();
    clearUserGraphByUserDefault();
}

UGCPoint KLineData::getUserGraphWithPoint(float x1, float y1, Rectangle rect, int startIndex)
{
    UGCPoint point;
    int index = rect.getViewIndexByPX(x1)+startIndex;

    if (hasData(index)) {
        point.dataY = rect.getValueY(y1);
        point.dataX = kLineData[index].dateTime;

        //最后条特殊处理
        if (index == getDataLength()-1 && closeDataFlag) {
            point.dataX = YTXTime::not_a_date_time();

            if (hasData(index-1)){
                point.preDataX = kLineData[index-1].dateTime;
            }
            else{
                point.preDataX = YTXTime::not_a_date_time();
            }
        }

    }
    else {
        point.dataY = -1;
    }

    return point;
}

int KLineData::getKLineItemIndexByDateTime(YTXTime time)
{
    for (vector<KLineItem>::iterator it = kLineData.begin() ; it != kLineData.end(); ++it) {
        KLineItem item = (*it);

        if (item.dateTime == time) {
            return (int)(it-kLineData.begin());
        }
    }

    return -1;
}

int KLineData::getDataLengthWithoutClose() const
{
    return getDataLength()-(closeDataFlag?1:0);
}

#pragma mark - Holding Position Infos

void KLineData::setHoldingPositionInfos(const std::list<HoldingPositionInfo> &holdingPositionOrders)
{
    this->holdingPositionInfos = holdingPositionOrders;
}

void KLineData::addHoldingPositionInfo(const HoldingPositionInfo &holdingPositionInfo)
{
    this->holdingPositionInfos.push_back(holdingPositionInfo);
}

void KLineData::clearHoldingPositionInfos()
{
    this->holdingPositionInfos.clear();
}

const std::list<HoldingPositionInfo> & KLineData::getHoldingPositionInfos() const
{
    return this->holdingPositionInfos;
}
